#ifndef CORE_BLOB_H_
#define CORE_BLOB_H_

#include <boost/shared_ptr.hpp>
#include <boost/make_shared.hpp>

#include "tbcore/predefined.hpp"
#include "basic_types.hpp"
#include "logging.hpp"

TB_NAMESPACE_BEGIN

class TB_BASE_API Blob {
public:
  typedef char value_type;

  typedef const char* const_iterator;

  typedef boost::shared_ptr<const value_type[]> buffer_const_ptr;

  typedef boost::shared_ptr<value_type[]> buffer_ptr;

  Blob();
  ~Blob();
  Blob(uint32 len);
	Blob(const char* data);
  Blob(const char* data, uint32 len);
  Blob(const buffer_const_ptr& buffer, uint32 len, uint32 offset = 0);
  Blob(const buffer_ptr& buffer, uint32 len, uint32 offset = 0);
  Blob(const Blob& other);
  Blob(Blob &&other);
  Blob &operator=(Blob &&rhs);
  Blob &operator=(const Blob &rhs);
  bool operator==(const Blob &rhs) const;
  void assign(const Blob& from, uint32 len, uint32 offset = 0);
  void assign(const buffer_const_ptr& buffer, uint32 len, uint32 offset = 0);
  void assign(const buffer_ptr& buffer, uint32 len, uint32 offset = 0);
  void assign(const char* data, uint32 len);
  const void* data() const;
  const char* content() const;
  uint32 size() const;
  void setSize(uint32 length);
  void clear();
  void swap(Blob& rhs);
  bool empty() const;
	Blob copy() const;
  buffer_const_ptr buffer() const;
  const_iterator begin() const;
  const_iterator end() const;

private:
  struct BlobImpl;
  BlobImpl* impl_;
};

TB_INLINE bool operator!=(const Blob& lhs, const Blob& rhs) {
  return !(lhs == rhs);
}

template <class A>
Blob merge(const A& allocator, const Blob& x, const Blob& y) {
  if (x.empty() || y.empty()) {
    return x.empty() ? y : x;
  }

  uint32 len = x.size() + y.size();
  Blob::buffer_ptr buffer = boost::allocate_shared<char[]>(allocator, len);
  ::memcpy(buffer.get(), x.data(), x.size());
  ::memcpy(buffer.get() + x.size(), y.data(), y.size());

  return Blob(buffer, len);
}

template <class _IT, class A>
Blob merge(const A& allocator, _IT begin, _IT end) {
  uint32 totalLength = 0;

  for (_IT it = begin; it != end; ++it) {
    totalLength += (*it).size();
  }

  if (0 == totalLength) {
    return Blob();
  } else if ((*begin).size() == totalLength) {
    return *begin;
  } else {
    Blob::buffer_ptr buffer = boost::allocate_shared<char[]>(allocator, totalLength);
    uint32 offset = 0;
    for (_IT it = begin; it != end; ++it) {
      ::memcpy(buffer.get() + offset, (*it).data(), (*it).size());
      offset += (*it).size();
    }

    DCHECK(offset == totalLength);
    return Blob(buffer, totalLength);
  }
}

TB_INLINE Blob merge(Blob& a, Blob& b) {
  return merge(std::allocator<char>(), a, b);
}

template <class _IT>
TB_INLINE Blob merge(_IT begin, _IT end) {
  return merge(std::allocator<char>(), begin, end);
}

TB_NAMESPACE_END

#endif // CORE_BLOB_H_
